package gao.test.tomcat.Digester.demo1;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.catalina.Server;
import org.apache.catalina.startup.CertificateCreateRule;
import org.apache.catalina.startup.ConnectorCreateRule;
import org.apache.catalina.startup.ContextRuleSet;
import org.apache.catalina.startup.EngineRuleSet;
import org.apache.catalina.startup.HostRuleSet;
import org.apache.catalina.startup.NamingRuleSet;
import org.apache.catalina.startup.SetAllPropertiesRule;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.RuleSet;
import org.xml.sax.InputSource;

import gao.test.tomcat.util.net.LogPropertiesTest;

public class AreaDigester {  
	
	
	public static void main(String[] args) {
		AreaDigester obj = new AreaDigester();
		
		
		try {
			obj.digester();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	
      
    public Digester digester() throws Exception {  
    	Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        List<String> attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);

        
        // Configure the actions we will be using
        // 根据Server标签创建Server对象，实例是StandardServer,设置属性，并且调用前一个对象的setServer方法，将StandardServer对象设置进去，上一个对象是什么，等下再讲解
    	
        // 1111111111
        digester.addObjectCreate("Server",                                          //**************注册要创建对象************//
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        // 创建NamingResources对象，并且设置到StandardServer中
        digester.addObjectCreate("Server/GlobalNamingResources",                      //**************注册要创建对象************//
                                 "org.apache.catalina.deploy.NamingResourcesImpl");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResourcesImpl");

      //遇到Listener标签，根据标签上className属性上类创建对应的对象，设置对应Listener的属性并且将该对象设置到StandardServer中
        digester.addObjectCreate("Server/Listener",                                   //**************注册要创建对象************//
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

       //遇到Service标签创建StandardService对象，然后设置其属性，然后将StandardService实例设置到StandardServer中
        digester.addObjectCreate("Server/Service",								      //**************注册要创建对象************//
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                            "addService",
                            "org.apache.catalina.Service");

        //遇到Service标签内部的Listener标签，跟上面一样创建对应的listener对象，然后设置到 StandardService对象中。
        digester.addObjectCreate("Server/Service/Listener",		                      //**************注册要创建对象************//
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        //Executor
        //创建对应的Executor 然后设置到StandardService中
        digester.addObjectCreate("Server/Service/Executor",				               //**************注册要创建对象************//
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        digester.addSetProperties("Server/Service/Executor");

        digester.addSetNext("Server/Service/Executor",
                            "addExecutor",
                            "org.apache.catalina.Executor");


        //22222222
        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
                new String[]{"executor", "sslImplementationName", "protocol"}));
        //将Connecter实例添加到 StandardService中
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");
        /**
         	标注2
         		我们在讲解tomcat启动的时候，讲到过Connector的初始化，但是当时我们并不知道Connector是如何初始化，
         		而且我们只查看了他的构造方法，知道Connector会根据协议的不同而去创建不同的对象。之所以需要关注代码2这里，
         		是因为这里就隐藏了Connector初始化的秘密。
         	
         	addRule这个方法我们之前讲解过，这个方法会给在Digester对象内部的规则对象(Rules对象，变量叫rules)中的
         	规则列表(一个hashmap)中针对摸个匹配模式添加一条规则！所以我们直接看ConnectorCreateRule对象即可。
         		
         		ConnectorCreateRule 继承了Rule对象，复写了begin方法，我们来查看具体源码
         */
        
        
        
        
        
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
                                 "org.apache.tomcat.util.net.SSLHostConfig");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig",
                "addSslHostConfig",
                "org.apache.tomcat.util.net.SSLHostConfig");

        
        
        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                         new CertificateCreateRule());
        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                         new SetAllPropertiesRule(new String[]{"type"}));
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                            "addCertificate",
                            "org.apache.tomcat.util.net.SSLHostConfigCertificate");

        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                                 "org.apache.tomcat.util.net.openssl.OpenSSLConf");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                            "setOpenSslConf",
                            "org.apache.tomcat.util.net.openssl.OpenSSLConf");

        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                                 "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                            "addCmd",
                            "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");

        
        //创建Connector 内部的listener
        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
                                  null, // MUST be specified in the element
                                  "className");
        digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
        digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
                            "addUpgradeProtocol",
                            "org.apache.coyote.UpgradeProtocol");

        //33333333333
        // Add RuleSets for nested elements
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
        /**
         	标注3
         		addRuleSet()方法之前文章中也讲解过这个方法，需要的参数是是一个RuleSet的实现类，我们看下EngineRuleSet
         		
         		EngineRuleSet 继承了RuleSetBase复写了addRuleInstances方法。  --  这一长串的代码其实做的就是一件事情，那就是完善创建StandardEngine对象并且设置内部各种属性和对象的引用。
         		
         		如果你继续查看HostRuleSet,ContextRuleSet的源码你会发现其实都是在做相似的事情，所以我们就对标注3的代码比较明了了
         		
         		
         	---	标注3的代码给我们充分展示了tomcat内部比较重要的几个对象Engine,Host,Context他们是如何创建的，他们之间的引用关系是如何被设置的，
         		可以看出Digester这个工具类解放了代码，可以使我们充分的利用xml文件特有的灵活，帮我们解决了对象之间的依赖关系，而我们不需要写代码，
         		只需要配置下xml文件即可！
         	---	前面说到Digester在启动时候会解析xml,其实在tomcat stop的时候也就是调用Catalina的stop()的时候也会创建一个Digeser对象，
         	          不过比较简单就留给读者自行理解了，当然tomcat不仅在解析server.xml的时候使用了Digester,同时tomcat在解析web.xml
         	          的时候也使用了Digester
         		
         */
        
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
            try {
            	// 读取tomcat config 下的 server.xml 配置文件。
                file = new File("src\\gao\\test\\tomcat\\Digester\\demo1\\server.xml");
                inputStream = new FileInputStream(file);
                LogPropertiesTest.debug("Catalina ： 读取tomcat config 下的 server.xml 配置文件 ： "+file.toURI().toURL().toString());
                
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e) {
            }
        
            
          /// 2222222222.
            inputSource.setByteStream(inputStream);
            
            //设置要封装的对象
            digester.push(this);
            
            
            
        digester.parse(inputSource);
    	
    	
        
        return digester;
    }  
    
        
    /**
     * The server component we are starting or stopping.
     */
    protected Server server = null;

    public void setServer(Server server) {
        
    	System.out.println("调用setServer方法");
    	this.server = server;
    }     
        
        
    
    /**
     * Cluster support is optional. The JARs may have been removed.
     */
    private static void addClusterRuleSet(Digester digester, String prefix) {
        Class<?> clazz = null;
        Constructor<?> constructor = null;
        try {
            clazz = Class.forName("org.apache.catalina.ha.ClusterRuleSet");
            constructor = clazz.getConstructor(String.class);
            RuleSet ruleSet = (RuleSet) constructor.newInstance(prefix);
            digester.addRuleSet(ruleSet);
        } catch (Exception e) {
        }
    }
}  